<template>
	<view class="next-list">
		<view class="next-search">
			<image class="next-search-img" :src="nextSearchImgSrc"></image>
			<input class="next-input" @input="search" v-model="searchStr" placeholder="请输入搜索信息" maxlength="50"
				placeholder-class="next-placeholder" />
		</view>
		<scroll-view 
			ref="scrollView"
			@scroll="scrollCallback" 
			class="next-scroll-left" 
			scroll-y="true" 
			:scroll-with-animation="true"
			:scroll-into-view="scrollIntoView"
		>
			<view id="TOP"><slot></slot></view>
			<view class="left-list" v-for="(item,index) of scrollLeftObj" :key="index" :id="getSafeId(index)">
				<view :id="'item_' + getSafeId(index)" class="left-item-title" v-if="item && item.length">{{index}}</view>
				<view class="left-item-card" v-for="(mess,inx) in item" :key="inx" @click.stop="chooseItem(mess)">
					<view v-if="showAvatar">
						<image :style="'border-radius:'+radius" class="left-item-card-img img-info" :src="mess[imgKey]"
							v-if="mess[imgKey]" @click.stop="preview(mess[imgKey])"></image>
						<view :style="'border-radius:'+radius" class="left-item-card-img" v-else>
							{{mess[nameKey] && mess[nameKey].slice(0,1) || ''}}
						</view>
					</view>	
					<view class="left-item-card-info"
						:style="inx<item.length-1?'border-bottom: solid #F4F4F4 1rpx;':''">
						<slot v-if="$slots.name" name="name" :data="mess"></slot>
						<view v-else class="left-item-card-name">{{mess[nameKey] || ''}}</view>
						<slot v-if="$slots.phone" name="phone" :data="mess"></slot>
						<view class="left-item-card-phone" v-else-if="mess[phoneKey]">{{mess[phoneKey]}}</view>
					</view>
				</view>
			</view>
			<view class="no-data" v-if="!hasData">
				<image class="no-data-img" :src="noDataImgSrc"></image>
				<view class="no-data-name">暂无数据</view>
			</view>
		</scroll-view>
		<view class="next-scroll-right" v-if="hasData">
			<image class="next-scroll-right-top" :src="nextScrollRightTopSrc" @click.stop="scrollTop"></image>
			<view :class="{'next-scroll-right-name':true,'next-scroll-right-select':item==scrollIntoViewCopy}"
				v-for="(item,index) in scrollRightList" :key="index" @click.stop="chooseType(item)">{{item}}
			</view>
		</view>
	</view>
</template>

<script>
	import nextSearchImgSrc from '../../static/search.png'
	import noDataImgSrc from '../../static/noData.png'
	import nextScrollRightTopSrc from '../../static/top.png'
	import pinyin from './js-pinyin/dist/pinyin.js'
	
	// 创建pinyin实例
	const pinyinInstance = new pinyin({charCase:0})
	
	const position = {}
	
	export default {
		props: {
			dataList: {
				type: Array,
				required: true,
				default: () => {
					return []
				}
			},
			//显示的主键key值
			idKey: {
				type: String,
				default: 'id'
			},
			nameKey: {
				type: String,
				default: 'name'
			},
			phoneKey: {
				type: String,
				default: 'phone'
			},
			imgKey: {
				type: String,
				default: 'img'
			},
			radius: {
				type: String,
				default: '6rpx'
			},
			showAvatar: {
				type: Boolean,
				default: true
			},
			isInterlock: {
				type: Boolean,
				default: true
			}
		},
		data() {
			return {
				searchStr: '',
				scrollIntoView: '',
				scrollIntoViewCopy: '',
				scrollLeftObj: {},
				oldObj: {},
				scrollRightList: [],
				hasData: true,
				nextSearchImgSrc,
				noDataImgSrc,
				nextScrollRightTopSrc,
				isScrolling: false // 添加滚动状态标记
			};
		},
		created() {
			// 监听数据变化
			this.$watch(() => this.dataList, this.watchDataList, {
				immediate: true,
				deep: true
			});
		},
		methods: {
			// 获取安全的ID
			getSafeId(index) {
				// 处理特殊字符，将#替换为更安全的标识符
				return index === '#' ? 'special' : index;
			},
			
			// 滚动回调
			scrollCallback(e) {
				if (!e || !e.detail) return;
				
				const { scrollTop, scrollHeight } = e.detail;
				if (this.scrollIntoView === 'TOP') {
					this.$nextTick(() => {
						this.scrollIntoView = '';
					});
				}
				
				if (this.isInterlock) {
					for (let key in position) {
						if (position[key] - scrollTop > 0 && position[key] - scrollTop <= scrollHeight) {
							this.scrollIntoViewCopy = key;
							break;
						}
					}
				}
			},
			
			// 滚动到顶部
			scrollTop() {
				if (this.isScrolling) return;
				this.isScrolling = true;
				
				this.$nextTick(() => {
					this.scrollIntoView = 'TOP';
					setTimeout(() => {
						this.isScrolling = false;
					}, 300);
				});
			},
			
			// 选择类型
			chooseType(item) {
				if (this.isScrolling) return;
				this.isScrolling = true;
				
				const targetId = item === '#' ? 'special' : item;
				this.$nextTick(() => {
					this.scrollIntoView = targetId;
					this.scrollIntoViewCopy = item;
					setTimeout(() => {
						this.isScrolling = false;
					}, 300);
				});
			},
			
			// 监听数据变化
			async watchDataList(newList) {
				if (!newList || !newList.length) return;
				
				await this.cleanData(newList);
				if (this.isInterlock) {
					this.$nextTick(() => {
						this.updatePositions();
					});
				}
			},
			
			// 更新位置信息
			updatePositions() {
				Object.keys(this.scrollRightList).forEach(key => {
					const safeId = this.getSafeId(this.scrollRightList[key]);
					const query = uni.createSelectorQuery().in(this);
					query.select(`#item_${safeId}`).boundingClientRect(res => {
						if (res) {
							position[this.scrollRightList[key]] = res.top;
						}
					}).exec();
				});
			},
			
			search() {
				if (this.searchStr) {
					let has = false
					this.scrollLeftObj = JSON.parse(JSON.stringify(this.oldObj))
					for (let i in this.scrollLeftObj) {
						this.scrollLeftObj[i] = this.scrollLeftObj[i].filter(item => {
							return (item[this.nameKey].indexOf(this.searchStr) != -1) || item[this.phoneKey]
								.indexOf(this.searchStr) != -1
						})
						if (this.scrollLeftObj[i].length) has = true
					}
					if (has) this.hasData = true
					else this.hasData = false
				} else {
					this.hasData = true
					this.scrollLeftObj = JSON.parse(JSON.stringify(this.oldObj))
				}
			},
			cleanData(list) {
				this.scrollRightList = this.getLetter()
				let newList = []
				list.forEach(res => {
					let initial = pinyinInstance.getCamelChars(res[this.nameKey].trim())
					let firsfirs = initial ? initial.substring(0, 1) : ''
					if (!newList[firsfirs]) newList[firsfirs] = []
					newList[firsfirs].push({
						[this.idKey]: res[this.idKey] || '',
						[this.nameKey]: res[this.nameKey].trim() || '',
						[this.phoneKey]: res[this.phoneKey] || '',
						[this.imgKey]: res[this.imgKey] || ''
					})
				})
				this.scrollRightList.forEach(t => {
					if (newList[t]) {
						this.$set(this.scrollLeftObj, t, newList[t])
					} else {
						this.$set(this.scrollLeftObj, t, [])
					}
				})
				let surplusList = []
				for (var i in newList) {
					let han = this.scrollRightList.find(v => {
						return v == i
					})
					if (!han) surplusList.push(newList[i])
				}
				surplusList.forEach(item => {
					this.scrollLeftObj['#'] = this.scrollLeftObj['#'].concat(item)
				})
				this.oldObj = JSON.parse(JSON.stringify(this.scrollLeftObj))
				
				// 过滤不存在的关键索引
				const existList = Object.keys(this.scrollLeftObj).filter(key => {
					return this.scrollLeftObj[key].length
				})
				this.scrollRightList = this.scrollRightList.filter(key => {
					return existList.some(k => k === key)
				})
			},
			getLetter() {
				let list = []
				for (var i = 0; i < 26; i++) {
					list.push(String.fromCharCode(65 + i))
				}
				list.push('#')
				return list
			},
			preview(img) {
				uni.previewImage({
					current: 0,
					urls: [img]
				})
			},
			chooseItem(item) {
				this.$emit('itemclick', item)
			}
		},
		beforeDestroy() {
			// 清理位置缓存
			Object.keys(position).forEach(key => {
				delete position[key];
			});
		}
	};
</script>
<style>
	:deep() ::-webkit-scrollbar {
		width: 0;
		height: 0;
		color: transparent;
		display: none;
	}
</style>
<style lang="scss" scoped>
.next-list {
  height: 100%;
  position: relative;
  background: #f8f8f8;
  
  .next-search {
    padding: 20rpx 30rpx;
    background: #fff;
    display: flex;
    align-items: center;
    box-shadow: 0 2rpx 10rpx rgba(0,0,0,0.05);
    
    .next-search-img {
      width: 32rpx;
      height: 32rpx;
      margin-right: 20rpx;
    }
    
    .next-input {
      flex: 1;
      height: 72rpx;
      font-size: 28rpx;
      color: #333;
    }
    
    .next-placeholder {
      color: #999;
    }
  }
  
  .next-scroll-left {
    height: calc(100% - 112rpx);
    
    .left-list {
      background: #fff;
      margin-bottom: 20rpx;
      
      .left-item-title {
        padding: 16rpx 30rpx;
        font-size: 24rpx;
        color: #999;
        background: #f8f8f8;
      }
      
      .left-item-card {
        display: flex;
        align-items: center;
        padding: 20rpx 30rpx;
        transition: all 0.3s;
        
        &:active {
          background: #f5f5f5;
        }
        
        .left-item-card-img {
          width: 80rpx;
          height: 80rpx;
          background: #eee;
          display: flex;
          align-items: center;
          justify-content: center;
          color: #999;
          font-size: 32rpx;
          margin-right: 24rpx;
          box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.1);
          
          &.img-info {
            object-fit: cover;
          }
        }
        
        .left-item-card-info {
          flex: 1;
          padding: 10rpx 0;
          
          .left-item-card-name {
            font-size: 30rpx;
            color: #333;
            margin-bottom: 8rpx;
            font-weight: 500;
          }
          
          .left-item-card-phone {
            font-size: 26rpx;
            color: #666;
          }
        }
      }
    }
  }
  
  .next-scroll-right {
    position: fixed;
    right: 20rpx;
    top: 50%;
    transform: translateY(-50%);
    z-index: 99;
    
    .next-scroll-right-top {
      width: 40rpx;
      height: 40rpx;
      margin-bottom: 10rpx;
    }
    
    .next-scroll-right-name {
      width: 40rpx;
      height: 40rpx;
      line-height: 40rpx;
      text-align: center;
      font-size: 24rpx;
      color: #666;
      border-radius: 50%;
      margin-bottom: 6rpx;
      transition: all 0.3s;
      
      &.next-scroll-right-select {
        background: #2B7FF3;
        color: #fff;
      }
    }
  }
  
  .no-data {
    padding-top: 200rpx;
    text-align: center;
    
    .no-data-img {
      width: 200rpx;
      height: 200rpx;
      margin-bottom: 20rpx;
    }
    
    .no-data-name {
      font-size: 28rpx;
      color: #999;
    }
  }
}
</style>